package tom.spring.cloud.gateway;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;

/**
 * 基于配置的gateway, profiles: host,hystrix,path,prefix,ratelimit
 * http://localhost:8091/actuator/gateway/routes
 *
 * 最终请求发到下游服务，由{@link org.springframework.cloud.gateway.filter.NettyRoutingFilter}发送
 * 配置文件gateway对应{@link org.springframework.cloud.gateway.config.GatewayProperties}由
 * {@link org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator}加载
 * @see <a href="https://cloud.spring.io/spring-cloud-gateway/spring-cloud-gateway.html#_deduperesponseheader_gatewayfilter_factory">https://cloud.spring.io/spring-cloud-gateway/spring-cloud-gateway.html#_deduperesponseheader_gatewayfilter_factory</a>
 * @see <a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html">https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html</a>
 * @author ZHUFEIFEI
 */
@Slf4j
@EnableWebFlux
@EnableDiscoveryClient
@SpringBootApplication
@ComponentScan(excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
                classes = {Gateway01Application.class, Gateway03Application.class})
})
public class Gateway02Application {

    public static void main(String[] args) {
        SpringApplication.run(Gateway02Application.class, args);
    }

    @RestController
    class GatewayController {
        @RequestMapping("/hystrixfallback")
        public String hystrixFallback(ServerHttpRequest request) {
            //注意此处不是ServletHttpRequest,是ServerHttpRequest,是reactive包中的实现
            request.getHeaders().forEach((k, v) -> {
                log.info("hystrix fallback header ==> {} : {}", k, StringUtils.join(v, ","));
            });
            return "This is a gateway inner hystrix fallback";
        }
    }

    /**
     * {@link org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration.EnableWebFluxConfiguration#DelegatingWebFluxConfiguration}
     *{@link org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration}
     * 此处配置了loggingRequestDetails会被覆盖，{@link org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration}
     * 通过配置文件配置即可，{@link org.springframework.boot.autoconfigure.http.HttpProperties}
     * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer
     *
     * @see org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfiguration
     * 启用EnableWebFluxSecurity后需要自定义WebFluxConfigurer来覆盖相关属性, 不再使用配置文件中的loggingRequestDetails属性
     */
    @Configuration
    class MyFluxConfigurer implements WebFluxConfigurer {
        @Override
        public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
            //请求头打印详情，默认打印{mask}，防止敏感信息泄露
            //此处配置了loggingRequestDetails会被后续custom配置覆盖, 但是当启用EnableWebFluxSecurity后就不会了
            //具体看源码 HttpWebHandlerAdapter中遍历ServerCodecConfigurer设置该值,所以如果最后一个不是true最终还是false
            configurer.defaultCodecs().enableLoggingRequestDetails(true);
        }
    }

    /**
     * {@link org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration}
     * {@link org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration}
     * reactive 安全认证配置, spring-cloud-gateway基于reactive的
     */
    @EnableWebFluxSecurity
    class MyWebReactiveSecurityConfig {
        @Bean
        public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
            //禁用csrf, SecurityWebFilterChain可以定义多个bean
            return http.csrf().disable()
                    .httpBasic().and()
                    .authorizeExchange().anyExchange().authenticated()
                    .and().formLogin().and()
                    .build();
        }

    }

}
